package com.gitee.wsl.transform.parser

import com.gitee.wsl.transform.parser.string.char
import com.gitee.wsl.transform.parser.string.int
import com.gitee.wsl.transform.parser.string.item
import com.gitee.wsl.transform.parser.string.number
import com.gitee.wsl.transform.parser.string.oneOrMany
import com.gitee.wsl.transform.parser.string.sat
import com.gitee.wsl.transform.parser.string.string
import com.gitee.wsl.transform.parser.string.takeWhile
import com.gitee.wsl.transform.parser.string.zeroOrMany

interface ParseScope {
    val leftover: String

    fun item(): StringParser<Char>
    fun sats(predicate: (Char) -> Boolean): StringParser<Char>
    fun char(c: Char): StringParser<Char>

    fun string(str: String): StringParser<String>
    fun takeWhile(predicate: (Char) -> Boolean): StringParser<String>

    //infix fun <T> StringParser<T>.or(other: StringParser<T>): StringParser<T>
    fun <T> zeroOrMany(parser: StringParser<T>): StringParser<List<T>>
    fun <T> oneOrMany(parser: StringParser<T>): StringParser<List<T>>

    suspend fun <T> StringParser<T>.bind(): T
    suspend fun <T> StringParser<T>.bindOptional(): T?
    suspend fun fail(): Nothing

    fun int(): StringParser<Int>
    fun double(): StringParser<Double>

    //fun <T> ignored(parser:StringParser<T>):StringParser<T>
}

class ParseScopeImpl(text: String) : ParseScope {
    private var _leftover: String = text
    override val leftover: String
        get() = _leftover

    override fun item(): StringParser<Char> = Parser.item()
    override fun sats(predicate: (Char) -> Boolean): StringParser<Char> = Parser.sat(predicate)
    override fun char(c: Char): StringParser<Char> = Parser.char(c)

    override fun string(str: String): StringParser<String> = Parser.string(str)

    override fun takeWhile(predicate: (Char) -> Boolean): StringParser<String> =Parser.takeWhile(predicate)
        /*zeroOrMany( Parser.sat(predicate)).flatMap {
            Parser.pure(it.joinToString(separator = ""))
        }*/

    //override fun <T> StringParser<T>.or(other: StringParser<T>): StringParser<T> = _or(other)

    override fun <T> zeroOrMany(parser: StringParser<T>): StringParser<List<T>> = Parser.zeroOrMany(parser)

    override fun <T> oneOrMany(parser: StringParser<T>): StringParser<List<T>> = Parser.oneOrMany(parser)

    override suspend fun <T> StringParser<T>.bind(): T = bindOptional() ?: fail()

    override suspend fun <T> StringParser<T>.bindOptional(): T? {
        val results = this.parser(leftover)
        return when (results.isEmpty()) {
            true -> null
            false -> {
                val first = results.first()
                _leftover = first.leftover
                first.value
            }
        }
    }

    override suspend fun fail(): Nothing = throw ParseError

    override fun int(): StringParser<Int> = Parser.int()

    override fun double(): StringParser<Double> = Parser.number()

    //override fun <T> ignored(parser: StringParser<T>):StringParser<T> {}
}

